package com.xyongfeng.foodManagement.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.benmanes.caffeine.cache.Cache;
import com.xyongfeng.foodManagement.common.ErrorCode;
import com.xyongfeng.foodManagement.domain.*;
import com.xyongfeng.foodManagement.domain.FoodOutInfo;
import com.xyongfeng.foodManagement.domain.vo.AdminInfoVO;
import com.xyongfeng.foodManagement.exception.BusinessException;
import com.xyongfeng.foodManagement.exception.ThrowUtils;
import com.xyongfeng.foodManagement.mapper.FoodOutInfoMapper;
import com.xyongfeng.foodManagement.service.AdminInfoService;
import com.xyongfeng.foodManagement.service.FoodInfoService;
import com.xyongfeng.foodManagement.service.FoodOutInfoService;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @author xyongfeng
 * @description 针对表【food_out_info】的数据库操作Service实现
 * @createDate 2023-12-17 17:02:37
 */
@Service
public class FoodOutInfoServiceImpl extends ServiceImpl<FoodOutInfoMapper, FoodOutInfo>
        implements FoodOutInfoService {

    @Autowired
    private AdminInfoService adminInfoService;
    @Autowired
    private FoodInfoService foodInfoService;

    @Autowired
    private Cache<Integer, FoodInfo> foodCache;

    /**
     * 阻塞队列
     */
    private final BlockingQueue<FoodOutInfo> orderTasks = new ArrayBlockingQueue<>(1024 * 1024);
    /**
     * 创建线程池
     */
    private final ExecutorService orderExecutor = Executors.newSingleThreadExecutor();
    private FoodOutInfoService proxy;

    /**
     * 当前类初始化完成之后执行
     */
    @PostConstruct
    private void init() {
        orderExecutor.submit(new CreateOrderHandler());
    }

    /**
     * 订单执行器
     */
    private class CreateOrderHandler implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    FoodOutInfo foodOutInfo = orderTasks.take();

                    // 因为这里调用了事务方法，要从AOP上下文中拿取代理接口再执行，否则会发送事务失效
                    proxy.createTable(foodOutInfo);

                } catch (InterruptedException e) {
                    log.error("创建订单异常", e);
                }

            }
        }
    }

    @Override
    public void insert(FoodOutInfo foodOutInfo, HttpServletRequest request) {
        Integer outFoodId = foodOutInfo.getOutFoodId();
        // 从缓存获取食品信息，如果没有则查询数据库

        FoodInfo insertFood;
        // 加锁保证原子性
        synchronized (foodCache) {
            insertFood = foodCache.getIfPresent(outFoodId);
            if (insertFood == null) {
                insertFood = foodInfoService.getById(outFoodId);
                foodCache.put(outFoodId, insertFood);
            }
            // 判断库存是否足够
            ThrowUtils.throwIf(foodOutInfo.getOutNum() > insertFood.getFoodStock(),
                    ErrorCode.PARAMS_ERROR, "库存不足");

            // 更新缓存
            insertFood.setFoodStock(insertFood.getFoodStock() - foodOutInfo.getOutNum());
            // 提前获取代理对象
            if (proxy == null) proxy = (FoodOutInfoService) AopContext.currentProxy();
        }

        // 获取管理id
        AdminInfoVO loginAdmin = adminInfoService.getLoginAdmin(request);
        ThrowUtils.throwIf(loginAdmin == null, new BusinessException(ErrorCode.NOT_LOGIN_ERROR));
        foodOutInfo.setOutAdminId(loginAdmin.getAdminId());

        // 设置时间
        long currentTimeMillis = System.currentTimeMillis();
        foodOutInfo.setOutTime(new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(currentTimeMillis));

        // 将订单加入阻塞队列
        orderTasks.add(foodOutInfo.clone());
    }


    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void createTable(FoodOutInfo foodOutInfo) {
        boolean save;
        Integer outFoodId = foodOutInfo.getOutFoodId();
        // 将食品库存减少
        save = foodInfoService.update(
                new UpdateWrapper<FoodInfo>()
                        .eq("food_id", outFoodId)
                        // 乐观锁条件
                        .gt("food_stock", 0)
                        .setSql(String.format("`food_stock` = `food_stock` - %d", foodOutInfo.getOutNum())));
        ThrowUtils.throwIf(!save, new BusinessException(ErrorCode.OPERATION_ERROR, "食品库存更新失败"));

        // 创建订单
        save = save(foodOutInfo);

        ThrowUtils.throwIf(!save, new BusinessException(ErrorCode.OPERATION_ERROR, "订单添加失败"));
    }

    @Override
    public List<FoodOutInfo> selectAll() {
        List<FoodOutInfo> list = lambdaQuery().orderByDesc(FoodOutInfo::getOutTime).list();
        // 绑定管理员与食品
        list = list.stream().peek((x) -> {
            AdminInfo adminInfo =
                    adminInfoService.getOne(new QueryWrapper<AdminInfo>()
                            .eq("admin_id", x.getOutAdminId()));
            FoodInfo foodInfo =
                    foodInfoService.getOne(new QueryWrapper<FoodInfo>()
                            .eq("food_id", x.getOutFoodId()));

            x.setAdminInfo(adminInfo);
            x.setFoodInfo(foodInfo);
        }).collect(Collectors.toList());
        return list;
    }
}




